package xworker.dataObject.data.tree;

import org.xmeta.ActionContext;
import org.xmeta.Thing;
import xworker.lang.executor.Executor;
import xworker.lang.util.Callback;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class TreeModel {
    private static final String TAG = TreeModel.class.getName();

    public static final String ROOT_ID = "__TreeNodeRootId__";

    /** 存储原始数据的Key，在原始数据被转成Map类型的TreeModel数据时 */
    public static final String Source = "_source";

    Thing thing;
    ActionContext actionContext;
    boolean rootVisible;
    boolean bindToParent;
    boolean loadBackground;
    boolean delayLoad;
    List<String> parentControls;
    TreeModelItem rootItem;
    List<TreeModelListener> listeners = new ArrayList<>();
    Object bindObjet;

    public TreeModel(Thing thing, ActionContext actionContext){
        this.thing = thing;
        this.actionContext = actionContext;

        rootVisible = thing.doAction("isRootVisible", actionContext);
        bindToParent = thing.doAction("isBindToParent", actionContext);
        parentControls = thing.doAction("getParentControls", actionContext);
        loadBackground = thing.doAction("isLoadBackground", actionContext);
        delayLoad = thing.doAction("isDelayLoad", actionContext);
    }

    /**
     * 从已加载的节点中寻找指定ID的节点。
     *
     * @param id 节点标识
     * @return 节点，不存在返回null
     */
    public TreeModelItem findItem(String id){
        if(id == null || id.isEmpty()){
            return null;
        }

        rootItem = getRoot();
        return findItem(rootItem, id);
    }

    private TreeModelItem findItem(TreeModelItem item, String id){
        if(id.equals(item.getId())){
            return item;
        }

        if(item.isChildInited()) {
            for (TreeModelItem childItem : item.getItems()) {
                TreeModelItem findedItem = findItem(childItem, id);
                if(findedItem != null){
                    return findedItem;
                }
            }
        }

        return null;
    }

    public Object getBindObjet() {
        return bindObjet;
    }

    public void setBindObjet(Object bindObjet) {
        this.bindObjet = bindObjet;
    }

    public void addListener(TreeModelListener listener){
        if(!listeners.contains(listener)){
            listeners.add(listener);
        }
    }

    public void removeListener(TreeModelListener listener){
        listeners.remove(listener);
    }

    public void fireItemUpdated(TreeModelItem item){
        for(TreeModelListener listener : listeners){
            listener.itemUpdated(item);
        }
    }

    public void fireItemRemoved(TreeModelItem item){
        for(TreeModelListener listener : listeners){
            listener.itemRemoved(item);
        }
    }

    public void fireItemAdded(TreeModelItem parentTreeModelItem, TreeModelItem treeModelItem, int index){
        for(TreeModelListener listener : listeners){
            listener.itemAdded(parentTreeModelItem, treeModelItem, index);
        }
    }

    public void fireItemRefreshed(TreeModelItem item){
        for(TreeModelListener listener : listeners){
            listener.itemRefreshed(item);
        }
    }

    public TreeModelItem getRoot(){
        if(rootItem != null){
            return rootItem;
        }
        rootItem = thing.doAction("getRoot", actionContext, "treeModel", this, "parentItem", null);
        List<TreeModelItem> items = new ArrayList<>();
        items.add(rootItem);
        thing.doAction("initItems", actionContext, "treeModel", this, "parentItem", null, "items", items);

        return rootItem;
    }

    public void loadChilds(final TreeModelItem parentItem, final Callback<List<TreeModelItem>, Void> callback) {
        if (loadBackground) {
            new Thread(() -> TreeModel.this.doLoadChilds(parentItem, callback)).start();
        } else {
            doLoadChilds(parentItem, callback);
        }
    }

    /**
     * 重新加载一个节点。
     *
     * @param item 要加载的节点，如果为null那么重新加载根节点
     */
    public void reload(TreeModelItem item){
        if(item == null){
            item = rootItem;
        }

        item.setItems(null);
        doLoadChilds(item, null);
        fireItemRefreshed(item);
    }

    /**
     * 在一个树节点的子节点的指定位置插入子节点。
     *
     * @param parentItem 父节点
     * @param index 子节点的索引位置，-1表示插入到模型，如果index大于子节点的个数也插入到末尾
     * @param sources 要插入的源对象列表
     */
    public void insert(TreeModelItem parentItem, int index, Object ... sources){
        if(sources == null || sources.length == 0){
            return;
        }

        List<TreeModelItem> items = thing.doAction("createBySources", actionContext, "sources", sources);
        if(items == null){
            Executor.warn(TAG, "Create items by sources return null, thing=" + thing.getMetadata().getPath() + ", sources=" + Arrays.toString(sources));
            return;
        }

        parentItem.insert(items, index);

        thing.doAction("initItems", actionContext, "treeModel", this, "parentItem", parentItem, "items", items);
        this.fireItemRefreshed(parentItem);
    }

    public void remove(TreeModelItem item){
        item.getParent().getItems().remove(item);

        this.fireItemRemoved(item);
    }

    public List<TreeModelItem> doLoadChilds(TreeModelItem parentItem, Callback<List<TreeModelItem>, Void> callback){
        List<TreeModelItem> items;
        if(parentItem.isChildInited()){
            items = parentItem.getItems();
        }else {
            items = thing.doAction("getChilds", actionContext,
                    "treeModel", this, "parentItem", parentItem);
            parentItem.setItems(items);
            thing.doAction("initItems", actionContext, "treeModel", this, "parentItem", parentItem, "items", items);

        }
        if(callback != null) {
            callback.call(items);
        }

        return items;
    }

    /**
     * 通过标识返回对应的条目。
     *
     * @param id　条目标识
     * @return　对应的条目，不存在返回null
     */
    public List<TreeModelItem> getItemById(String id){
        return thing.doAction("getItemById", actionContext, "treeModel", this, "parentItem", null, "id", id);
    }

    public Thing getThing() {
        return thing;
    }

    public ActionContext getActionContext() {
        return actionContext;
    }

    public TreeModelItem getRootItem() {
        return rootItem;
    }

    public boolean isBindToParent() {
        return bindToParent;
    }

    public List<String> getParentControls() {
        return parentControls;
    }

    public boolean isRootVisible() {
        return rootVisible;
    }

    public boolean isLoadBackground() {
        return loadBackground;
    }

    public boolean isDelayLoad() {
        return delayLoad;
    }

    public static TreeModel create(ActionContext actionContext){
        Thing self = actionContext.getObject("self");

        Thing treeModel = self;
        while(true) {
            Thing ref = self.doAction("getTreeModelRef", actionContext);
            if(ref != null){
                treeModel = ref;
            }else{
                break;
            }
        }

        return new TreeModel(treeModel, actionContext);
    }
}
